/*****************************************************
******************************************************
EEPROM_Test Joe Pardue 4/30/10

This program demonstrates how to use avrlibc functions to
read and write AVR EEPROM. 

It was written to support the Nuts&Volts July 2010
Smiley's Workshop 24: AVR Memory Part 2 EEPROM

If you find this useful, go to www.smileymicros.com 
and buy something.
******************************************************
******************************************************/

// I HATE LICENSES LIKE THIS >>BUT<< I've been told that without
// the license then the work is automatically copyrighted in my name
// since my purpose is to educate, I want the code to be used by whoever
// wants to use it to learn something. If you like it, then visit
// my website www.smileymicros.com and buy something. Did I already say that?

/*
 *  BSD License
 *  -----------
 *
 *  Copyright (c) 2008, Smiley Micros, All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer. 
 *   
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution. 
 *   
 *  - Neither the name of the Smiley Micros nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission. 
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 *  ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */

// And to further cover my butt, let me add that if you use this software
// it will destroy whatever machine you use it on and kill anyone in a one 
// kilometer radius. So don't even consider using it for any reason whatsoever!
// And go to www.smileymicros.com and --- wait for it --- buy something.


/*
Since this code was written for three different devices it is cluttered with
a lot of #if defined compiler directives to accomodate the different devices
clocking and USART register names. If find the #if directives make the code
more difficult to follow than writing a single version for each device, but
it makes the code easier to write, test, and maintain. Most of this should be 
hidden in include files or libraries - and eventually will, but for todays
lesson, you get to learn about #if even though it is irrelevant to EEPROM
You are welcome. Now go to - okay you know the drill.
*/


/*****************************************************
	Includes and declarations from UART_Test
******************************************************/
#include <avr/io.h>
#include <avr/pgmspace.h> 
#include <avr/eeprom.h>  
//#include <avr/interrupt.h> //Butterfly

// Remove the comment directive '//' from the device you want
// to use this code on.
//#define Arduino	// Arduino board with Arduino IDE - 57600 baud
//#define ATmega328 // Arduino board with AVRStudio/WinAVR - 57600 baud
//#define ATmega644 // BeAVR board - 16 MHz clock - 57600 baud
#define Butterfly // The good old Butterfly - ATmega169 - 19200 baud 

// IF YOU ARE USEING AVRSTUDIO MAKE SURE YOU set the correct device
// in the Project/Configuration Options window. 

#if defined(Butterfly)
#include <avr/interrupt.h>
//#define F_CPU 8000000
//#define BAUD_RATE 19200
#elif defined(Arduino) || defined (ATmega328) || defined (ATmega644) //RS added "||defined (ATmega328) || defined(ATmega644)"
#define F_CPU 16000000L
#define BAUD_RATE 57600
#endif

// We have to include these declarations outside of the #if defined
// but the actual definitions will only be used if the Butterfly
// is defined.
void OSCCAL_calibration(void);
void Delay(unsigned int millisec);

// Common declarations for all devices
void sayHello(); //Keep track of the version you are using
void USARTInit();
void USARTTransmit( unsigned char );
unsigned char USARTReceive( void );

void setup();
void loop();

// The Arduino IDE provides the main() functio for you
// but you need this for AVRStudio/WinAVR
//#if !defined(Arduino)			// RS removed
int main(void) 
{ 
	setup();
	loop();
}
//#endif						// RS removed

// We use setup() and loop() so that we can use define statements
// to run the same code in the device list including the Arduino
void setup()
{
	USARTInit();
	sayHello();
}

void loop()
{
	uint8_t c;
	uint16_t address;
	uint8_t byteValue;
	uint16_t wordValue;

	while(1)
	{
		c = USARTReceive();

		if( c == 'b') // Read Byte 
		{
			// Send command byte 0x62
			// Send address byte
			// Receive data byte

			address = USARTReceive();

			USARTTransmit(eeprom_read_byte((uint8_t*)address));			
		}
		else if(c == 'B') // Write Byte
		{
			// Send command byte 0x42
			// Send address byte
			// Send byte to write

			address = USARTReceive();

			byteValue = USARTReceive();

			//debug
			//USARTTransmit(byteValue);

			eeprom_busy_wait();
			eeprom_write_byte((uint8_t*)address,byteValue);
		}
		else if(c == 'w') // Read Word
		{			
			// Send command byte 0x77
			// Send address high byte
			// Send address low byte
			// Receive word high byte
			// Receive word low byte

			address = (USARTReceive() << 8);
			address |= USARTReceive();

			wordValue = eeprom_read_word((uint16_t*)address);

			USARTTransmit(wordValue>>8);
			USARTTransmit(wordValue);			
		}
		else if(c == 'W') // Write Word
		{
			// Send command byte 0x57
			// Send address high byte
			// Send address low byte
			// Send word high byte
			// Send word low byte
					
			address = (USARTReceive() << 8);
			address |= USARTReceive();

			wordValue = (uint16_t)(USARTReceive()<<8);
			wordValue |= (uint16_t)USARTReceive();

			eeprom_busy_wait();
			eeprom_write_word((uint16_t*)address,wordValue);
		}
		else if(c == 'k') // Read Block of 10 bytes starting at address 0
		{
			// Send command byte 'k' 0x6b
			// Receive 10 bytes from location

			uint8_t localBlock[10]; 		
			uint16_t size = 10; 
			uint16_t address = 0;

			eeprom_read_block((void*)&localBlock,(const void*)address,size);				
		
			int i;
			for(i=0;i<size;i++)
			{
				USARTTransmit(localBlock[i]);
			}
		}
		else if(c == 'l') // Write 10 byte test block
		{
			// Send command byte 'l' 0x6c to write this block

			uint8_t localBlock[] = {'A','B','C','D','E','F','G','H','I','J'};
			uint16_t size = 10;
			uint16_t address = 0;

			eeprom_write_block((const void*)localBlock,(void*)address,size);
	
		}
		else if(c == 'm') // Write 10 byte test block
		{
			// Send command byte 'm' 0x6d to write this block

			uint8_t localBlock[] = {'0','1','2','3','4','5','6','7','8','9'};
			uint16_t size = 10;
			uint16_t address = 0;

			eeprom_write_block((const void*)localBlock,(void*)address,size);
		}

		else USARTTransmit('?');
	}
}

// This is just so you can change the number and verify
// that you actually loaded the version you intended.
void sayHello()
{
	USARTTransmit('E');
	USARTTransmit('8');
	USARTTransmit('\n');
}

/*****************************************************
	USART functions to send and receive bytes
	between a PC terminal and the AVR
******************************************************/

void USARTInit()
{
#if defined(Arduino) || defined(ATmega328) || defined(ATmega644)
	UBRR0H = (F_CPU/(BAUD_RATE*16L)-1) >> 8;
	UBRR0L =(uint8_t)(F_CPU/(BAUD_RATE*16L)-1);

	/* Enable receiver and transmitter */
	UCSR0B = (1<<RXEN0)|(1<<TXEN0);

	/* Set frame format: 8data, 2stop bit */
	//UCSR0C = (1<<USBS0)|(3<<UCSZ00);

#elif defined(Butterfly)
// This is mostly 'borrowed' from the original Butterfly
	/* Set baud rate hard coded to 19200 */
	UBRRL = 38;
	/* Enable receiver and transmitter */
	UCSRB = (1<<RXEN)|(1<<TXEN);
	/* Set frame format: 8data, 2stop bit */
	UCSRC = (1<<USBS)|(3<<UCSZ0);

    OSCCAL_calibration();   // Calibrate the OSCCAL byte

    ACSR = (1<<ACD);

    // Disable Digital input on PF0-2 (power save)
    DIDR1 = (7<<ADC0D);

    PORTB = 0xFF;       // Enable pullups on Ports B and E
    PORTE = 0xFF;
    
    // boost IntRC to 2Mhz to achieve 19200 baudrate
    CLKPR = (1<<CLKPCE);        // set Clock Prescaler Change Enable
    // set prescaler = 4, Inter RC 8Mhz / 4 = 2Mhz
    CLKPR = (1<<CLKPS1);    

    UBRRH = 0;//(unsigned char)(baudrate>>8);
    UBRRL = 12;//(unsigned char)baudrate;

    // Enable 2x speed
    UCSRA = (1<<U2X);

    // Enable receiver and transmitter
    UCSRB = (1<<RXEN)|(1<<TXEN)|(0<<RXCIE)|(0<<UDRIE);

    // Async. mode, 8N1
    UCSRC = (0<<UMSEL)|(0<<UPM1)|(0<<USBS)|(3<<UCSZ0)|(0<<UCPOL);
        
    MCUCR = (1<<IVCE);       
    MCUCR = (1<<IVSEL);             //move interruptvectors to the Boot sector    

    sei(); // mt __enable_interrupt();
    
    // mt PCMSK1 = (1<<PORTB6) | (1<<PORTB4);
	PCMSK1 = (1<<PINB6) | (1<<PINB4);       // set pin-change interrupt mask
  	EIFR   = (1<<PCIF1);                    // clear external intterupt flag 1
	EIMSK  = (1<<PCIE1);   

#endif

}



void USARTTransmit( unsigned char data )
{

#if defined(Arduino) || defined(ATmega328) || defined(ATmega644)  // RS added "|| defined(ATmega644)"
	/* Wait for empty transmit buffer */
	while ( ( UCSR0A & (1<<UDRE0)) == 0 );
	/* Put data into buffer, sends the data */
	UDR0 = data;
#elif defined(Butterfly)
	/* Wait for empty transmit buffer */
	while ( !( UCSRA & (1<<UDRE)) );
	/* Put data into buffer, sends the data */
	UDR = data;
#endif
}

unsigned char USARTReceive( void )
{

#if defined(Butterfly)
	/* Wait for data to be received */
	while ( !(UCSRA & (1<<RXC)) );
	/* Get and return received data from buffer */
	return UDR;
#elif defined(Arduino) || defined(ATmega328) || defined(ATmega644)  // RS added "|| defined(ATmega644)"
	/* Wait for data to be received */
	while ( (UCSR0A & (1<<RXC0)) == 0 );

	/* Get and return received data from buffer */
	return UDR0;
#endif
}

#if defined(Butterfly)
// The Butterfly uses the internal RC circuit for the F_CPU clock
// It calibrates this with a 32768 Hz watch crystal
void OSCCAL_calibration(void)
{
    unsigned char calibrate = 0;//FALSE;
    int temp;
    unsigned char tempL;

    CLKPR = (1<<CLKPCE);        // set Clock Prescaler Change Enable
    // set prescaler = 8, Inter RC 8Mhz / 8 = 1Mhz
    CLKPR = (1<<CLKPS1) | (1<<CLKPS0);
    
    TIMSK2 = 0;             //disable OCIE2A and TOIE2

    ASSR = (1<<AS2);        //select asynchronous operation of timer2 (32,768kHz)
    
    OCR2A = 200;            // set timer2 compare value 

    TIMSK0 = 0;             // delete any interrupt sources
        
    TCCR1B = (1<<CS10);     // start timer1 with no prescaling
    TCCR2A = (1<<CS20);     // start timer2 with no prescaling

    while((ASSR & 0x01) | (ASSR & 0x04));       //wait for TCN2UB and TCR2UB to be cleared

    Delay(1000);    // wait for external crystal to stabilise
    
    while(!calibrate)
    {
        cli(); // mt __disable_interrupt();  // disable global interrupt
        
        TIFR1 = 0xFF;   // delete TIFR1 flags
        TIFR2 = 0xFF;   // delete TIFR2 flags
        
        TCNT1H = 0;     // clear timer1 counter
        TCNT1L = 0;
        TCNT2 = 0;      // clear timer2 counter
           
	while ( !(TIFR2 & (1<<OCF2A)) ); // while ( !(TIFR2 && (1<<OCF2A)) );   // wait for timer2 compareflag
    
        TCCR1B = 0; // stop timer1

        sei(); // __enable_interrupt();  // enable global interrupt
    
        if ( (TIFR1 & (1<<TOV1)) ) // if ( (TIFR1 && (1<<TOV1)) )
        {
            temp = 0xFFFF;      // if timer1 overflows, set the temp to 0xFFFF
        }
        else
        {   // read out the timer1 counter value
            tempL = TCNT1L;
            temp = TCNT1H;
            temp = (temp << 8);
            temp += tempL;
        }
    
        if (temp > 6250)
        {
            OSCCAL--;   // the internRC oscillator runs to fast, decrease the OSCCAL
        }
        else if (temp < 6120)
        {
            OSCCAL++;   // the internRC oscillator runs to slow, increase the OSCCAL
        }
        else
            calibrate = 1;//TRUE;   // the interRC is correct
    
        TCCR1B = (1<<CS10); // start timer1
    }
}


/*****************************************************************************
*
*   Function name : Delay
*
*   Returns :       None
*
*   Parameters :    unsigned int millisec
*
*   Purpose :       Delay-loop
*
*****************************************************************************/
void Delay(unsigned int millisec)
{
    unsigned char i; // mt int i;
    
    while (millisec--)
        for (i=0; i<125; i++);
}
#endif

